<?php

namespace app\api\service;

use app\common\Common;
use Overtrue\EasySms\EasySms;
use think\Exception;
use think\facade\Cache;
use think\facade\Log;

class SmsService
{
    // 登录验证码
    const TYPE_LOGIN = 'SMS_462680196';

    //限定一个ip一天最多发送短信次数
    const IP_SEND_SMS_TIMES = 10;

    const TYPES = [
        self::TYPE_LOGIN,
    ];

    /**
     * 发送验证码
     * @param string $mobile
     * @param string $template
     * @return array|void
     */
    public static function sendCode(string $mobile, string $template = self::TYPE_LOGIN)
    {
        $code = self::genCode();
        try {
            $ip = Common::getClientIp();
            $date = date('Ymd');
            $ipKey = "sms_send_frequency:{$template}:{$ip}:{$date}";
            $limitKey = "mobile_sms_send_limit:{$mobile}";
            $redis = Cache::store('redis')->handler();
            $redis->select(15);
            $times = $redis->get($ipKey) ?? 0;
            $limit = $redis->get($limitKey) ?? 0;
            //检查是否相隔60秒后发送
            if ($limit == 0){
                $redis->set($limitKey, 1, 60);
            }else{
                $ttl = $redis->ttl($limitKey);
                throw new Exception('发送太频繁，'.$ttl.'秒后才能重新发送短信验证码!');
            }
            //检查发送次数
            if ($times == 0){
                $redis->set($ipKey, 1,strtotime(date('Y-m-d 23:59:59')) - time());
            }else{
                if ($times >= self::IP_SEND_SMS_TIMES){
                    throw new Exception('今日短信验证码发送次数过多，请明天重试');
                }else{
                    $redis->incr($ipKey);
                }
            }

            $easySms = new EasySms(config('sms'));

            $easySms->send($mobile, [
                'template' => $template,
                'data' => [
                    'code' => $code,
                ],
            ]);

            self::rememberCode($mobile, $code, 300, $template);
            return ['code' => '200', 'message' => '发送成功'];
        } catch (\Exception $e) {
            $message = $e->getMessage();
            if (method_exists($e, 'getExceptions')) {
                $message = $e->getExceptions()["aliyun"]->getMessage();
            }
            flog([
                'message' => '短信发送失败：'.$message,
                'phone' => $mobile,
                'template' => $template,
            ],'sms','error');
            return ['code' => '400', 'message' => $message];
        }
    }

    /**
     * 生成验证码
     * @param int $length
     * @return string
     */
    public static function genCode(int $length = 6): string
    {
        $code = str_pad(mt_rand(0, pow(10, $length) - 1), $length, '0', STR_PAD_LEFT);
        return str_shuffle($code);
    }

    /**
     * 验证码校验
     *
     * @param $mobile
     * @param $code
     * @param bool $delete
     * @param string $template
     * @return bool
     */
    public static function verifyCode($mobile, $code, bool $delete = true, string $template = self::TYPE_LOGIN): bool
    {
        $redis = Cache::store('redis')->handler();
        $redis->select(15);
        $rememberCode = $redis->get(self::getCodeCacheKey($mobile, $template));

        if ($code == $rememberCode) {
            //验证成功删除缓存
            $delete && $redis->del(self::getCodeCacheKey($mobile, $template));

            return true;
        }

        return false;
    }

    /**
     * 获取验证码
     * @param $mobile
     * @param string $template
     * @return string
     */
    public static function getCodeCacheKey($mobile, string $template = self::TYPE_LOGIN): string
    {
        if ($template != self::TYPE_LOGIN) {
            return 'sms:' . $mobile . '_' . $template;
        }
        return 'sms:' . $mobile;
    }

    /**
     * 记住验证码
     * @param $mobile
     * @param $code
     * @param int $seconds
     * @param string $template
     * @return void
     */
    public static function rememberCode($mobile, $code, int $seconds = 300, string $template = self::TYPE_LOGIN)
    {
        $redis = Cache::store('redis')->handler();
        $redis->select(15);
        $redis->set(self::getCodeCacheKey($mobile, $template), $code,$seconds);
    }

}
